
#ifndef __RTCN_RUDPBASE_H__
#define __RTCN_RUDPBASE_H__

#include "RtNetworkBase.h"
#include "RtConnectionInterface.h"
#include "RtMessageBlock.h"

//////////////////////////////////////////////////////////////////////////

#define RUDP_INFO_TRACE(str)      RT_INFO_TRACE("[RUDP] "<<str) 
#define RUDP_WARNING_TRACE(str)   RT_WARNING_TRACE("[RUDP] "<<str) 
#define RUDP_ERROR_TRACE(str)     RT_ERROR_TRACE("[RUDP] "<<str) 

#define RUDP_INFO_TRACE_THIS(str)      RT_INFO_TRACE_THIS("[RUDP] "<<str) 
#define RUDP_WARNING_TRACE_THIS(str)   RT_WARNING_TRACE_THIS("[RUDP] "<<str) 
#define RUDP_ERROR_TRACE_THIS(str)     RT_ERROR_TRACE_THIS("[RUDP] "<<str) 

//////////////////////////////////////////////////////////////////////////

/// Define __BITSTREAM_NATIVE_END to NOT support endian swapping in the BitStream class.  This is faster and is what you should use
/// unless you actually plan to have different endianness systems connect to each other
/// Enabled by default.
#define __BITSTREAM_NATIVE_END

/// Number of ordered streams available. You can use up to 32 ordered streams
#define NUMBER_OF_ORDERED_STREAMS 32 // 2^5

#define RESEND_TREE_ORDER 32

const int PING_TIMES_ARRAY_SIZE = 5;

enum RtRudpState {
	RUDP_STATE_INIT,
	RUDP_STATE_CLOSED,
	RUDP_STATE_SYNC_SENT,
	RUDP_STATE_SYNC_RECV,
	RUDP_STATE_CONNREQ_SENT,
	RUDP_STATE_OPEN,
};

typedef BYTE RtRudpPduType;
enum  {
	RUDP_PDU_SYNC,
	RUDP_PDU_CONNREQ,
	RUDP_PDU_CONNRESP,
	RUDP_PDU_SHUTDOWN,
	RUDP_PDU_KEEPALIVE,
	RUDP_PDU_RESET,

	RUDP_PDU_DATA = 7,
	RUDP_PDU_DROPPACKET,
	RUDP_PDU_PUNCH, // Punch through NAT
};

#define RUDP_PACKET_PADDING ((BYTE)0xFF)

#define RUDP_TIME_64BIT 1

#ifdef RUDP_TIME_64BIT
typedef LONGLONG RtRudpTime; // Millisecond
typedef LONGLONG RtRudpTimeUS; // Microsecond
#else
typedef unsigned int RtRudpTime; // Millisecond
typedef LONGLONG RtRudpTimeUS; // Microsecond
#endif

typedef unsigned short RtRudpPacketNo;
typedef unsigned short RtRudpOrderingID;
typedef unsigned short RtRudpSplitPacketNo;
typedef unsigned short RtRudpSplitPacketID;

#define BITS_TO_BYTES(x) (((x)+7)>>3)
#define BYTES_TO_BITS(x) ((x)<<3)

struct RtRudpPdu
{
public:
	///PDU type
	RtRudpPduType pduType;
	///PDU data
	CRtMessageBlock* data;
	///The priority level of this packet
	IRtTransport::DataPriority priority;
	///What type of reliability algorithm to use with this packet
	RtRudpReliability reliability;
	///What ordering channel this packet is on, if the reliability type uses ordering channels
	unsigned char orderingChannel;
	///Time-to-live value of the packet, in milliseconds
	INT ttl;

public:
	RtRudpPdu()
		: data(NULL)
	{
		Reset();
	}
	void Reset()
	{
		pduType = 0;
		if (data)
		{
			data->DestroyChained();
			data = NULL;
		}
		priority = IRtTransport::DP_MEDIUM;
		reliability = RUDP_UNRELIABLE;
		orderingChannel = 0;
		ttl = 0;
	}
};

//////////////////////////////////////////////////////////////////////////

#define RUDP_TIME_FROM_GetTimeOfDay

extern "C"
{
	inline RtRudpTimeUS GetRtRudpTimeUS()
	{
#ifndef RUDP_TIME_FROM_GetTimeOfDay
		static bool initialized=false;
#ifdef RT_WIN32
		static DWORD mProcMask;
		static DWORD mSysMask;
		static HANDLE mThread;
		static RtRudpTimeUS lastQueryVal=(RtRudpTimeUS)0;
		static LARGE_INTEGER yo;
#else 
		static CRtTimeValue tp, initialTime;
#endif

		if ( initialized == false )
		{
#ifdef RT_WIN32
			HANDLE mProc = GetCurrentProcess();
			// Get the current Affinity
			GetProcessAffinityMask(mProc, &mProcMask, &mSysMask);
			mThread = GetCurrentThread();

			QueryPerformanceFrequency( &yo );
#else
			initialTime = CRtTimeValue::GetTimeOfDay();
#endif
			initialized = true;
		}

#ifdef RT_WIN32
		// Set affinity to the first core
		SetThreadAffinityMask(mThread, 1);

		LARGE_INTEGER PerfVal;
		QueryPerformanceCounter( &PerfVal );

		// Reset affinity
		SetThreadAffinityMask(mThread, mProcMask);

		__int64 quotient, remainder;
		quotient=((PerfVal.QuadPart) / yo.QuadPart);
		remainder=((PerfVal.QuadPart) % yo.QuadPart);
		RtRudpTimeUS curTime = (RtRudpTimeUS) quotient*(RtRudpTimeUS)1000000 + (remainder*(RtRudpTimeUS)1000000 / yo.QuadPart);
		if (curTime<lastQueryVal)
		{
			RUDP_ERROR_TRACE("::GetRtRudpTimeUS, counter error! curTime="<<curTime<<" lastQueryVal="<<lastQueryVal);
			return lastQueryVal;
		}
		lastQueryVal = curTime;
		return curTime;
#else
		tp = CRtTimeValue::GetTimeOfDay();
		return (tp.GetSec()-initialTime.GetSec()) * (RtRudpTimeUS) 1000000 + (tp.GetUsec()-initialTime.GetUsec());
#endif

#else // #ifndef RUDP_TIME_FROM_GetTimeOfDay
		return (RtRudpTimeUS)CRtTimeValue::GetTimeOfDay().GetTotalInUsec();
#endif // #ifndef RUDP_TIME_FROM_GetTimeOfDay
	}

	inline RtRudpTime GetRtRudpTime()
	{
#ifndef RUDP_TIME_FROM_GetTimeOfDay
		return (RtRudpTime)(GetRtRudpTimeUS()/1000);
#else
		return (RtRudpTime)CRtTimeValue::GetTimeOfDay().GetTotalInMsec();
#endif
	}

};

//////////////////////////////////////////////////////////////////////////

#endif // __RTCN_RUDPBASE_H__
